//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<xeres_engine.cpp>
///	@path	~/src/engine/xcore/
///	@date	2007/09/26
///	@desc	Engine integration.

#include "config/config.h"
#include "config/version.h"

#include "lib/utilities/debug.h"
#include "lib/utilities/observer.h"
#include "lib/system/sysinfo.h"
#include "lib/system/thread_debug.h"

#include "database/xdata/CTreeData.h"

#include "graphics/IEffect.h"
#include "graphics/IGeometry.h"

#include "graphics/dx9/CDevice9.h"
#include "graphics/dx10/CDevice10.h"

#include "engine/xcore/xeres_engine.h"
#include "engine/xcore/event_center.h"

#define	ENGINE_PARALLEL_TICK		1
#define	ENGINE_PARALLEL_RENDER		2

#if (defined _WIN64)
#	define	SYSTEM_CONTEXT				((void*)(0xff1a2b3c4d5e6f70LL))
#else
#	define	SYSTEM_CONTEXT				((void*)(0xffa2b3c4d1))
#endif

TRACE_CATEGORY( _S("Engine/xcore/core") , TRACE_LV_TRACE );

namespace xeres {

	// ctor
	Engine::Engine( void )
		: m_inited( false )
		, m_firstFrame( false )
		, m_updating( false )
	{
		TRACE_INFO( 
			_S("\n-------------------------------------------------------\n")
			_S("%S-------------------------------------------------------\n") ,
			XERES_ENGINE_INFO_STRING );

		// initialize main thread
		Thread::InitMain();

		// setup thread debug, main thread
		DEFINE_THREAD_CHECK( _S("main") );
	}

	// dtor
	Engine::~Engine( void )
	{
		TRACE_INFO( _S("Xeres Engine has been finalized...") );
	}

	// instance
	Engine& Engine::instance( void )
	{
		static Engine s_global_xeres_engine;
		return s_global_xeres_engine;
	}

	// fromFrame
	Engine& Engine::fromFrame( const Frame& frame )
	{
		return *( static_cast<Engine*>( const_cast<Frame*>(&frame) ) );
	}

	// config device for engine
	void Engine::ConfigDevice( void * main_window , const WString& device , const WString& renderer ,
			RefWeak<IData> device_config , RefWeak<IData> renderer_config )
	{
		TRACK_FUNCTION_SCOPE();
		// define device thread
		DEFINE_THREAD_CHECK( _S("device") );

		if( m_device.IsValid() )
		{
			TRACE_ERROR( _S("Engine::ConfigDevice: Device has been configurated.") );
			return;
		}

		IObject * object = NULL;

		XS_ASSERT( m_device.IsNull() );
		m_device = ObjectFactory::CreateObject( device );

		XS_ASSERT( m_renderer.IsNull() );
		m_renderer = ObjectFactory::CreateObject( renderer );

		// initialize device
		m_device->Initialize( main_window , device_config );

		// initialize renderer
		m_renderer->Initialize( m_device , renderer_config );

		RenderContext::Setup( m_device , m_renderer );
	}

	// ConfigMultiTasking
	void Engine::ConfigMultiTasking( RefWeak<IData> pool_config )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("main") );

		if( TaskManager::IsInitialized() )
		{
			TRACE_ERROR( _S("Engine::ConfigMultiTasking: Multi-tasking has been configurated.") );
			return;
		}

		// default to 4 helper threads
		int thread_count = 4;
		if( pool_config.IsValid() )
			pool_config->ReadInt( _S("multitasking/background_thread") , 4 );

		// ! maximize to 20 additional threads, enough for any game design !
		thread_count = max( 1 , min( thread_count , 20 ) );

		// load parallelize strategy
		WString strategy = _S("none");
		if( pool_config.IsValid() )
			pool_config->ReadString( _S("multitasking/parallelize") , _S("none") );

		if( strategy != _S("auto") && strategy != _S("none") &&
			strategy != _S("tick") && strategy != _S("render") )
		{
			TRACE_ERROR( _S("Engine::ConfigMultiTasking: Unrecognized parallelize strategy '%s', default to 'auto'.") ,
				strategy.c_str() );
			strategy = _S("auto");
		}

		// process threading core config
		bool config_affinity = false;
		if( pool_config.IsValid() )
			pool_config->ReadBool( _S("multitasking/affinity") , false );

		LoopScheduler::ConfigMultiTasking( strategy , thread_count , config_affinity );
	}

	// ConfigScene
	void Engine::ConfigScene( RefWeak<IData> scene_config )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("device") );

		LogicManager::Initialize( this , scene_config );
	}

	// Engine helpers.
	namespace
	{
		//	starts thread pool when initialize threads
		void _start_thread_pool_callback( ThreadPool * threadPool )
		{
			TRACE_INFO( _S("Starting threads ...") );
		}

		// perthread initialize
		void _perthread_init_callback( void )
		{
			TRACE_INFO( _S("New thread established.") );
		}

		// perthread finalize
		void _perthread_fini_callback( void )
		{
			TRACE_INFO( _S("Leaving thread ...") );
		}

		// main thread initialize per-thread
		void _main_thread_init_perthread( ThreadPool * threadPool )
		{
			threadPool->InitializeThisThread( "main" );
		}

		// main thread finalize per-thread
		void _main_thread_fini_perthread( ThreadPool * threadPool )
		{
			threadPool->FinalizeThisThread();
		}

		// finalize renderer
		void _renderer_fini_callback( void )
		{
		}

		// register basic classes
		void _register_basic_classes( void )
		{
			TRACE_INFO( _S("Registering basic classes...") );
			// graphics
			// console
			// engine
			//IOBJECT_REGISTER_CLASS( CVar );
		}
	}

	void Engine::Initialize( void )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("main") );

		// check inited
		if( m_inited )
		{
			TRACE_ERROR( _S("Engine::Initialize: Initialize is called.") );
			return;
		}

		TRACE_INFO( _S("Initializing engine...") );

		// event center
		EventCenter::Initialize();

		// timer center
		TimerCenter::Initialize();

		// initialize resources
		ResourceCenter::Initialize( this );
		
		// add system defined initializer

		// register basic classes
		QueueInitializer( _S("./register/class") , _S("") , _register_basic_classes );
		// generate per-thread list
		QueueInitializer( _S("thread/per-thread/.") , _S("") , bind( _perthread_init_callback ) );
		// initialize main thread
		QueueInitializer( _S("thread/start/main-thread") , _S("") , bind( _main_thread_init_perthread , this ) );

		// start thread pool
		QueueInitializer( _S("thread/start/.") , _S("") , bind( _start_thread_pool_callback , this ) );
		// initialize renderer
		QueueInitializer( _S("renderer/.") , _S("") , bind( &Engine::InitializeRenderer , this ) );

		// add system defined finalizer

		// ends main thread per-thread call
		QueueFinalizer( _S("thread/main") , _S("") , bind( _main_thread_fini_perthread , this ) );
		// per thread finalize call
		QueueFinalizer( _S("thread/per-thread/.") , _S("") , _perthread_fini_callback );
		// finalize renderer
		QueueFinalizer( _S("renderer/.") , _S("") , _renderer_fini_callback );

		// add system defined game looper

		InsertStage( _S("$/.") , _S("") , NULL );
		InsertStage( _S("$/input") , _S(".") , NULL );
		InsertStage( _S("$/message") , _S("input") , NULL );
		InsertStage( _S("$/device") , _S("message") , NULL );

		InsertStage( _S("input/.") , _S("") , NULL );
		InsertStage( _S("input/event") , _S("") , NULL );
		InsertStage( _S("input/timer") , _S("") , bind( &TimerCenter::Tick , this , _1 ) );

		InsertStage( _S("logic/.") , _S("") , bind( &LogicManager::Tick , this , _1 ) );
		InsertStage( _S("logic/scenario") , _S(".") , NULL );
		InsertStage( _S("logic/entity") , _S("") , bind( &EntityManager::Tick , this , _1 ) );

		InsertStage( _S("scene/.") , _S("") , bind( &SceneManager::Tick , this , _1 ) );
		InsertStage( _S("scene/world") , _S(".") , NULL ) ;
		InsertStage( _S("scene/present") , _S("world") , bind( &Engine::Present , this ) );

		InsertStage( _S("debug/.") , _S("") , NULL );

		InsertStage( _S("ui/.") , _S("") , NULL );

		InsertStage( _S("final/.") , _S("") , NULL );
		InsertStage( _S("final/cull") , _S("") , bind( &Engine::Cull , this ) );

		InsertStage( _S("sync/.") , _S("") , NULL );
		InsertStage( _S("sync/resource") , _S(".") , bind( &Engine::UpdateSyncResourceCall , this ) );

		// engine debug update
		InsertStage( _S("debug/engine") , _S("") , bind( &EngineDebug::Tick , this , _1 ) );

		// mark initialized
		m_inited = true;

		// for debug use

		// add render debug hook
//		RenderManager::AddRenderHook( bind( &Engine::Render , this , _1 ) );
	}

	// InitializeRenderer
	void Engine::InitializeRenderer( void )
	{
		TRACE_INFO( _S("Initializing renderer...") );
		RenderManager::Initialize();
	}

	void Engine::FirstFrame( void )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("main") );

		// check first frame called
		if( m_firstFrame )
		{
			TRACE_ERROR( _S("Engine::FirstFrame: FirstFrame is called.") );
			return;
		}

		TRACE_INFO( _S("Engine: First frame...") );

		// invoke initializers
		Initors::FirstFrame();

		// check!
		if( m_device.IsNull() )
		{
			TRACE_ERROR( _S("Engine::FirstFrame: Device is not created.") );
			FATAL_ABORT( _S("Device is not created.") );
		}

		if( !SceneManager::IsInitialized() )
		{
			TRACE_ERROR( _S("Engine::FirstFrame: Scene manager is not configurated.") );
			FATAL_ABORT( _S("Scene manager is not configurated.") );
		}

		if( !TaskManager::IsInitialized() )
		{
			TRACE_ERROR( _S("Engine::FirstFrame: Thread pool is not initialized.") );
			FATAL_ABORT( _S("Thread pool is not initialized.") );
		}

		// engine debugger
		EngineDebug::Initialize( m_renderer );

		m_firstFrame = true;

		// update frame control
		FrameControl::FirstFrame( 0 );

		// update loop schedule
		LoopScheduler::FirstFrame();
	}

	void Engine::LastFrame( void )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("main") );

		// check first frame called
		if( !m_firstFrame )
		{
			TRACE_ERROR( _S("Engine::LastFrame: FirstFrame is not called.") );
			return;
		}

		TRACE_INFO( _S("Last frame...") );

		// Clear loop scheduler.
		LoopScheduler::LastFrame();

		// game logics
		LogicManager::Finalize();

		// execute stages
		Initors::LastFrame();

		// engine debugger
		EngineDebug::Finalize();

		m_firstFrame = false;
	}

	void Engine::Finalize( void )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("main") );

		// check inited
		if( !m_inited )
		{
			TRACE_ERROR( _S("Engine::Finalize: Initialize is not called.") );
			return;
		}

		TRACE_INFO( _S("Finalizing engine...") );

		// render manager
		RenderManager::Finalize();
		// task manager
		TaskManager::Finalize();
		// resource center
		ResourceCenter::Finalize();

		// renderer
		if( m_renderer.IsValid() )
		{
			m_renderer->Finalize();

			// release
			//m_renderer->ReleaseRef();
			m_renderer = NULL;
		}

		// device
		if( m_device.IsValid() )
		{
			m_device->Finalize();

			// release
			//m_device->ReleaseRef();
			m_device = NULL;
		}

		// timer center
		TimerCenter::Finalize();

		// event center
		EventCenter::Finalize();

		m_loopSyncResourceCalls.clear();
		m_inited = false;
	}

	// update frame
	void Engine::UpdateFrame( void )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("main") );

COUNTER_GUARD( _S("engine") );

		Counter start = Counter::Now();

		XS_ASSERT( m_updating == false );
		m_updating = true;
		LoopScheduler::UpdateFrame();
		m_updating = false;

		Counter diff = Counter::Now() - start;

		StatEngineTime( diff.ToMilliSecond() );
	}

	// Present
	void Engine::Present( void )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("main|tick|!pool*|!_render") );

		COUNTER_GUARD( _S("present") );
		SceneManager::Present( *this );
	}

	// Cull
	void Engine::Cull( void )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("main|tick|!pool*|!_render") );

		COUNTER_GUARD( _S("cull") );
		BeginBatch();
		Culler::CullPresentation( *this );
		EndBatch();
	}

	// resources

	void Engine::QueueSyncResourceCreator( function<void(RenderManager&)> res_call )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("main|tick|!pool*|!_render") );

		// check thread
		m_loopSyncResourceCalls.push_back( res_call );
	}

	void Engine::UpdateSyncResourceCall( void )
	{
		COUNTER_GUARD( _S("sync_res") );

		// run only once
		std::deque< function<void(RenderManager&)> >& calls =
			m_loopSyncResourceCalls;
		for( size_t i = 0 , total = calls.size() ; i < total ; ++i )
		{
			calls[i]( *this );
		}
		calls.clear();
	}

	// LoadScene
	void Engine::PreLoadScene( const WString& name , function<void(TaskResult)> complete_call )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("main|tick|!pool*|!_render") );

		TaskManager::InsertAsyncTask( bind( &LogicManager::PreLoadScene , this , name ) , complete_call );
	}

	// InstantScene
	bool Engine::InstantScene( const WString& name )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("main|tick|!pool*|!_render") );

		TRACE_WARNING( _S("Engine::InstantScene: Main thread IO may cause performance problem.") );

		bool result;

		Counter start = Counter::Now();
		do{
			// preload scene
			if( !(result = SceneManager::PreLoadScene( name ) ) ) break;

			// and construct it

		}while(0);
		Counter diff = Counter::Now() - start;

		// Give performance warning if needed.

		// active scene
		ActiveScene( name );

		return result;
	}

} // namespace xeres
